package org.jeecg.modules.weixin.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.SecurityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.MinioUtil;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.order.entity.KuybOrder;
import org.jeecg.modules.order.entity.KuybOrderChanp;
import org.jeecg.modules.order.entity.KuybOrderPayment;
import org.jeecg.modules.order.service.IKuybOrderChanpService;
import org.jeecg.modules.order.service.IKuybOrderPaymentService;
import org.jeecg.modules.order.service.IKuybOrderPolicyService;
import org.jeecg.modules.order.service.IKuybOrderService;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.weixin.service.SubscribeMessageService;
import org.jeecg.modules.weixin.vo.DecryptResource;
import org.jeecg.modules.weixin.vo.Resource;
import org.jeecg.modules.weixin.vo.WeixinPayCallBack;
import org.jeecg.modules.zhifhdjl.entity.KuybZhifhdjl;
import org.jeecg.modules.zhifhdjl.service.IKuybZhifhdjlService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.*;

import static org.jeecg.common.util.MD5Util.byteArrayToHexString;

@Slf4j
@RestController
@RequestMapping("/weixin")
public class WeiXinController {
    @Autowired
    IKuybOrderPaymentService kuybOrderPaymentService;

    @Autowired
    IKuybOrderChanpService kuybOrderChanpService;

    @Autowired
    SubscribeMessageService subscribeMessageService;

    @Autowired
    IKuybOrderService kuybOrderService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    RestTemplate restTemplate;

    @Value("${weixin.appId}")
    String _appId = "";

    @Value("${weixin.secret}")
    String _secret = "";

    @Value("${weixin.mchId}")
    String _mchId = "";

    @Value("${weixin.payKey}")
    String _payKey = "";

    @Value("${weixin.merchantSerialNumber}")
    String _merchantSerialNumber = "";

    @Value("${weixin.apiV3Key}")
    String _apiV3Key;

    @Value("${weixin.apiclientKeyPath}")
    String _apiclientKeyPath;

    //        HttpClient httpClient = builder.build();
    CloseableHttpClient httpClient = null;
    AutoUpdateCertificatesVerifier verifier = null;
    PrivateKey privateKey = null;

    @GetMapping("/getPrepayIdAndPaySignByDingdbh")
    public Result<?> getPrepayIdAndPaySignByDingdbh(String dingdbh) {
        KuybOrder order = kuybOrderService.getOne(new LambdaQueryWrapper<KuybOrder>().eq(KuybOrder::getDingdbh, dingdbh), false);
        if (order != null) {
            Map<String, Object> map = new HashMap<>();
            map.put("signType", "RSA");
            map.put("prepay_id", order.getPrepayId());
            map.put("paySign", order.getPaySign());
            map.put("timeStamp", order.getTimeStamp() + "");
            map.put("nonceStr", order.getNonceStr());

            return Result.OK(map);
        }

        return Result.error("没找到数据");
    }

    @SneakyThrows
    public WeiXinController(@Value("${weixin.mchId}") String _mchId,
                            @Value("${weixin.merchantSerialNumber}") String _merchantSerialNumber, @Value("${weixin.apiV3Key}") String _apiV3Key, @Value("${weixin.apiclientKeyPath}") String _apiclientKeyPath) {
        privateKey = PemUtil.loadPrivateKey(new FileInputStream(_apiclientKeyPath));

        //不需要传入微信支付证书了
        verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(_mchId, new PrivateKeySigner(_merchantSerialNumber, privateKey)),
                _apiV3Key.getBytes("utf-8"));

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(_mchId, _merchantSerialNumber, privateKey)
                .withValidator(new WechatPay2Validator(verifier));

        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(_mchId, _merchantSerialNumber, privateKey)
                .withValidator(response -> true) // NOTE: 设置一个空的应答签名验证器，**不要**用在业务请求
                .build();
    }

    @SneakyThrows
    private String getAccessToken() {
        HttpGet httpGet = new HttpGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + _appId + "&secret=" + _secret);
        CloseableHttpResponse response = httpClient.execute(httpGet);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        log.info(bodyAsString);
        if (bodyAsString != null) {
            JSONObject jsonObject = JSONObject.parseObject(bodyAsString);
            return jsonObject.getString("access_token");
        }

        return null;
    }

    @SneakyThrows
    @GetMapping("/getUnlimited2Minio")
    public Result<?> getUnlimited2Minio(String mobile) {
        String ACCESS_TOKEN = getAccessToken();

        HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + ACCESS_TOKEN);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("scene", "shareBy=" + mobile)
                .put("page", "pages/home/home");

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        byte[] b = EntityUtils.toByteArray(response.getEntity());
        InputStream is = new ByteArrayInputStream(b);
        String downloadPath = MinioUtil.upload(is, "xiaocxm_" + mobile + ".png");

        SysUser sysUser = sysUserService.getUserByPhone(mobile);
        if (sysUser != null) {
            sysUser.setMyerm(downloadPath);
            sysUserService.updateById(sysUser);
        }

        return Result.OK(downloadPath);
    }

    @SneakyThrows
    @GetMapping("/getUnlimited")
    public void getUnlimited(String mobile, HttpServletResponse hsr) {
        String ACCESS_TOKEN = getAccessToken();

        HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + ACCESS_TOKEN);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("scene", "shareBy=" + mobile)
                .put("page", "pages/home/home");

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        byte[] b = EntityUtils.toByteArray(response.getEntity());
        OutputStream os = hsr.getOutputStream();
        os.write(b);
        os.flush();

    }


    String md5(String in) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(in.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }

            return sb.toString().toUpperCase();
        } catch (Exception e) {
            ;
        }

        return "";
    }

    @GetMapping("/refund")
    public Result<?> refund(String orderId) {
        KuybOrder kuybOrder = kuybOrderService.getById(orderId);
        if (kuybOrder == null)
            return Result.error("没有找到对应的订单");

        // 退费中
        kuybOrder.setUpdateTime(new Date());
        kuybOrder.setDingdzt("2");
        kuybOrderService.updateById(kuybOrder);
        return Result.OK();
    }

    @SneakyThrows
    @PostMapping("/getPrepayIdAndPaySign")
    public Result<?> getPrepayIdAndPaySign(@RequestBody KuybOrder order) {
//        String appid = "wx9308044613fae7f8";
//        String paykey = "b979655a134bd955c185676ab43523aa";
//        String macid = "1249480601";
//        omDVs5S44MD6HOGsTLgboOjwAjKk

        String prepayid = "";
        JSONObject jsonObject = getPrepayId(order);
        if (jsonObject != null && jsonObject.getString("code") != null && jsonObject.getString("code").equals("PARAM_ERROR"))
            return Result.error(500, jsonObject.getString("message"));

        if (jsonObject.getString("prepay_id") != null)
            prepayid = jsonObject.getString("prepay_id");

        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("INVALID_REQUEST"))
            return Result.error(jsonObject.getString("message"));

        Long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = UUID.randomUUID().toString().replace("-", "");

//        timeStamp = 1632321294l;
//        nonceStr = "97d92c48c4ec432eaaee00ee802f6b66";
//        prepayid = "wx22223452765071c4b0b27aae5c09300000";

        String paySign = _appId + "\n"
                + timeStamp + "\n"
                + nonceStr + "\n"
                + "prepay_id=" + prepayid + "\n";
        System.out.println(paySign);

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(this.privateKey);
        sign.update(paySign.getBytes());
        paySign = java.util.Base64.getEncoder().encodeToString(sign.sign());
//        System.out.println("WAF+E9mwrKufJsim5hA+ybqILBZNTZzz9Lnc6Oom/DIrTQ/bqwYNxG9VTTEeLgPeFDPLgAOedruIKRtr84RFfGFx0u+nm36pV1xq3es2aLlQh3LAje4SajFe/vzRB82Gqn1oTG6oTI0/jZs6OZ8u4SLoB2XxB0mrCu5T+ilQ6VwS4rRRzY3VGy9Y4XMzYXAjhFPel+0iSjnQdo8tyhDUSJb8UMm9U02khNKivXODF7QQ6YC2bOj442eOZ7I8yp586eOJAFzX6UnQ2kf3lPz6VrFZGl0iHnPyLU9ShZzLMJ0NyZgnGFSlFXf80xQLUuzKaq2lGDLwaAGaZJeyAF/89A==");
//        System.out.println(paySign);

        Map<String, Object> map = new HashMap<>();
        map.put("signType", "RSA");
        map.put("prepay_id", prepayid);
        map.put("paySign", paySign);
        map.put("timeStamp", timeStamp + "");
        map.put("nonceStr", nonceStr);

        log.info("订单编号: " + order.getDingdbh());
        order = kuybOrderService.getOne(new LambdaQueryWrapper<KuybOrder>().eq(KuybOrder::getDingdbh, order.getDingdbh()), false);
        if (order != null) {
            order.setPrepayId(prepayid);
            order.setPaySign(paySign);
            order.setTimeStamp(timeStamp + "");
            order.setNonceStr(nonceStr);

            // 我的订单中的待支付，1小时内重新发起支付用
            kuybOrderService.updateById(order);
        }

        return Result.OK(map);
    }

    @Autowired
    IKuybZhifhdjlService kuybZhifhdjlService;

    // 支付回调
    @PostMapping("/notifyUrl")
    public String notifyUrl(HttpServletRequest request, HttpServletResponse response) {
        String msgJson = "";
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"));
            String rtn = br.readLine();
            while (rtn != null) {
                msgJson += rtn;
                rtn = br.readLine();
            }

            log.info("msgJson, {}", msgJson);
            // {"id":"c0b3a280-c161-57ac-9d6b-80d225eb594c","create_time":"2021-09-23T08:33:57+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"l4bmU+T8SSusmMXx2OtYqfoLZV5nkz+kilcqPq1wOmROgQiB8zxMyUGMdIqf73RkC+H/pwvyzdKxFm3qSDFfdSct6tolL37FC/pDxwkFTVq5WPQSacBcKFtwIm+4XpVBa22ixE2ho4jK8r1r0ApaesfhCsgDOJGghGxGkRToL8dfuYCmWmL0RhJGcwkLyEw824xIJo7dAw7EOBTZIh+rl0K1m2kxroROoW0XQvj9A9FiAo5skha3aPNDTAYq9WDKEj1V+mwXByiDS4hmx1SOCb9VogqGyKWq6foykRGR6avd1vCQEOl5eYM/OPh1EdtGCqJiyxJkeEiXbwh3VzjaY5K8m++zZOAVNzx0HjGHO7/0XcLLcFrC6FQBJa8d6Oas9n7EzexnmHLLb2HWqc3izI84eNwBpMxDpza6f3HoZkqC5XLAvpGQ97kFQZ4M9EdULv8WD1t2PW03XXTliMA5nuO4neiuhedgJ381POnhmymNQSHW1K6t0imdT6eoGo2smq051S+rChdTUF8kE0jYb0Gz0Mmwkl9UxlImZ/5k/1ba8Hj/XaXG6A==","associated_data":"transaction","nonce":"bZfiQCIGW4vw"}}
            WeixinPayCallBack weixinPayCallBack = JSON.parseObject(msgJson, new TypeReference<WeixinPayCallBack>() {
            });
            if (weixinPayCallBack != null) {
                X509Certificate wechatpayCertificate = verifier.getValidCertificate();

                try {
                    Resource resource = weixinPayCallBack.getResource();

                    AesUtil aesUtil = new AesUtil(_apiV3Key.getBytes());
                    String ciphertext = aesUtil.decryptToString(resource.getAssociated_data().getBytes(), resource.getNonce().getBytes(), resource.getCiphertext());
                    log.info(ciphertext);
                    DecryptResource decryptResource = JSON.parseObject(ciphertext, new TypeReference<DecryptResource>() {
                    });
                    if (decryptResource != null) {
                        String dingdbh = decryptResource.getOut_trade_no();
                        KuybOrder kuybOrder = kuybOrderService.getOne(new LambdaQueryWrapper<KuybOrder>().eq(KuybOrder::getDingdbh, dingdbh), false);
                        if (kuybOrder != null) {
                            List<KuybOrderChanp> kuybOrderChanpList = kuybOrderChanpService.selectByMainId(kuybOrder.getId());
                            kuybOrder.setKuybOrderChanpList(kuybOrderChanpList);

                            // 订单状态已支付
                            kuybOrder.setDingdzt("1");
                            kuybOrder.setUpdateTime(new Date());

                            KuybOrderPayment kuybOrderPayment = new KuybOrderPayment();
                            kuybOrderPayment.setDingdbh(kuybOrder.getDingdbh());
                            kuybOrderPayment.setZhubid(kuybOrder.getId());

                            kuybOrderPayment.setZhiffs("1");
                            kuybOrderPayment.setJiaoylsh(decryptResource.getTransaction_id());
                            kuybOrderPayment.setMchid(decryptResource.getMchid());
                            kuybOrderPayment.setAppid(decryptResource.getAppid());
                            kuybOrderPayment.setOpenid(decryptResource.getPayer().getOpenid());
                            kuybOrderPayment.setZhifje(decryptResource.getAmount().getTotal());
                            kuybOrderPayment.setCreateTime(DateUtil.parse(decryptResource.getSuccess_time()));

                            if (!kuybOrderPaymentService.save(kuybOrderPayment))
                                log.error("保存支付回调失败");

                            if (!kuybOrderService.updateById(kuybOrder))
                                log.error("更新订单失败");

                            // 支付回调记录，防止丢失支付回调信息
                            KuybZhifhdjl kuybZhifhdjl = new KuybZhifhdjl();
                            kuybZhifhdjl.setDingdbh(kuybOrder.getDingdbh());
                            kuybZhifhdjl.setJiaoylsh(decryptResource.getTransaction_id());
                            kuybZhifhdjl.setMchid(decryptResource.getMchid());
                            kuybZhifhdjl.setAppid(decryptResource.getAppid());
                            kuybZhifhdjl.setOpenid(decryptResource.getPayer().getOpenid());
                            kuybZhifhdjl.setZhifje(decryptResource.getAmount().getTotal());
                            kuybZhifhdjlService.save(kuybZhifhdjl);


                            // 模板消息，留一个审核通过的通知就行了
//                            ObjectMapper objectMapper = new ObjectMapper();
//                            ObjectNode objectNode = objectMapper.createObjectNode();
//
//                            ObjectNode value = objectMapper.createObjectNode();
//                            value.put("value", kuybOrder.getDingdbh());
//                            objectNode.set("character_string1", value);
//
//                            value = objectMapper.createObjectNode();
//                            value.put("value", DateUtil.formatDateTime(new Date()));
//                            objectNode.set("date15", value);
//
//                            value = objectMapper.createObjectNode();
//                            value.put("value", kuybOrder.getDingdje());
//                            objectNode.set("amount16", value);
//
//                            value = objectMapper.createObjectNode();
//                            value.put("value", kuybOrder.getKuybOrderChanpList().get(0).getChanpmc());
//                            objectNode.set("thing12", value);
//                            subscribeMessageService.send(kuybOrder.getOpenId(), "wuXf0CBiF_hIL_oKGynlVWGOWjmcVOsAE0n3XqVUqC4", objectNode);

                            response.setCharacterEncoding("utf-8");
                            response.getWriter().write("{\"code\": \"SUCCESS\",\"message\": \"成功\"}");
                            log.info("已发送回调应答");
                        }

                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(e.getMessage() + e.getLocalizedMessage());
                    response.setCharacterEncoding("utf-8");
                    response.getWriter().write("{\"code\": \"ERROR\",\"message\": \"" + e.getMessage() + e.getLocalizedMessage() + "\"}");
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage() + e.getLocalizedMessage());
        }

        return null;
    }

    private static String getParamString(Map map, String key) {
        String buf = "";
        if (map.get(key) instanceof String[]) {
            buf = ((String[]) map.get(key))[0];
        } else {
            buf = map.get(key) + "";
        }
        return buf;
    }

    /**
     * @param xml
     * @return Map
     * @description 将xml字符串转换成map
     */
    public static Map<String, String> readStringXmlOut(String xml) {
        Map<String, String> map = new HashMap<String, String>();
        Document doc = null;
        try {
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML
            Element rootElt = doc.getRootElement(); // 获取根节点
            @SuppressWarnings("unchecked")
            List<Element> list = rootElt.elements();// 获取根节点下所有节点
            for (Element element : list) { // 遍历节点
                map.put(element.getName(), element.getText()); // 节点的name为map的key，text为map的value
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * MD5编码
     *
     * @param origin 原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(resultString.getBytes("UTF-8"));
            resultString = byteArrayToHexString(md.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

    /**
     * 对参数列表进行排序，并拼接key=value&key=value形式
     *
     * @param map
     * @return
     */
    private static String sortParameters(Map<String, Object> map) {
        Set<String> keys = map.keySet();
        List<String> paramsBuf = new ArrayList<String>();
        for (String k : keys) {
            paramsBuf.add((k + "=" + getParamString(map, k)));
        }
        // 对参数排序
        Collections.sort(paramsBuf);
        String result = "";
        int count = paramsBuf.size();
        for (int i = 0; i < count; i++) {
            if (i < (count - 1)) {
                result += paramsBuf.get(i) + "&";
            } else {
                result += paramsBuf.get(i);
            }
        }
        return result;
    }

    /**
     * 生成签名sign
     * 第一步：非空参数值的参数按照参数名ASCII码从小到大排序，按照键值对的形式。生成字符串StringA
     * stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
     * 第二部：拼接API密钥，这里的秘钥是微信商户平台的秘钥，是自己设置的，不是公众号的秘钥
     * stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
     * 第三部：MD5加密
     * sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
     *
     * @param map 不包含空字符串的map
     * @return
     */
    public String sign(Map<String, Object> map, String key) {
        //排序
        String sort = sortParameters(map);
        //拼接API秘钥
        sort = sort + "&key=" + key;
        //System.out.println(sort);
        //MD5加密
        System.out.println("微信签名" + sort);
        String sign = MD5Encode(sort).toUpperCase();
        return sign;
    }

    public static String mapToXml(Map<String, Object> arr) {
        StringBuffer xml = new StringBuffer();
        xml.append("<xml>");
        Iterator<Map.Entry<String, Object>> iter = arr.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, Object> entry = iter.next();
            String k = entry.getKey();
            Object v = entry.getValue();
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)
                    || "sign".equalsIgnoreCase(k) || "notify_url".equalsIgnoreCase(k)
                    || "ToUserName".equals(k) || "FromUserName".equals(k)
                    || "desc".equalsIgnoreCase(k)
                    || "MsgType".equals(k)) {
                xml.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                xml.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        xml.append("</xml>");
        return xml.toString();
    }

    @SneakyThrows
    JSONObject getPrepayId(KuybOrder order) {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", _mchId)
                .put("appid", _appId)
                .put("description", order.getProductName())
                .put("notify_url", "https://xiaocx-dev.kuyingauto.com/jeecg-boot/weixin/notifyUrl")
                .put("out_trade_no", order.getDingdbh());

        rootNode.putObject("amount")
                .put("total", order.getDingdje());
        // 支付1分钱测试用
   /*     rootNode.putObject("amount")
                .put("total", 1);*/

        rootNode.putObject("payer")
                .put("openid", order.getOpenId());

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        log.info("bodyAsString, {}", bodyAsString);
        return JSONObject.parseObject(bodyAsString);
    }

    @GetMapping("/bindMobile")
    public Result bindMobile(String openId, String sessionKey, String iv, String encryptedData, String nickName, String avatarUrl) {
        SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getOpenId, openId), false);
        if (sysUser != null) {
            // 生成token
            String token = JwtUtil.sign(sysUser.getUsername(), sysUser.getPassword());
            sysUser.setToken(token);

            // 设置token缓存有效时间
            redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
            redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);

            return Result.OK(sysUser);
        }

        AES aes = new AES(Mode.CBC, Padding.NoPadding, Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
        String resultByte = aes.decryptStr(Base64.decodeBase64(encryptedData), Charset.forName("utf-8"));
        JSONObject jsonObject = JSON.parseObject(resultByte);

        String phoneNumber = "";
        if (jsonObject != null)
            phoneNumber = jsonObject.getString("phoneNumber");

        sysUser = new SysUser();
        sysUser.setDelFlag(0);
        sysUser.setUsername(phoneNumber);
        sysUser.setRealname(phoneNumber);
        sysUser.setPassword("123456");
        sysUser.setSalt("12345678");
        sysUser.setAvatar(avatarUrl);

        String userpassword = PasswordUtil.encrypt(sysUser.getUsername(), sysUser.getPassword(), sysUser.getSalt());
        sysUser.setPassword(userpassword);

        sysUser.setOpenId(openId);
        sysUser.setPhone(phoneNumber);
        sysUser.setStatus(1);

        if (sysUserService.save(sysUser)) {
            // 生成token
            String token = JwtUtil.sign(sysUser.getUsername(), sysUser.getPassword());
            sysUser.setToken(token);

            // 设置token缓存有效时间
            redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
            redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);

            return Result.OK(sysUser);
        } else
            return Result.error(500, "绑定手机号失败");
    }

    @GetMapping("/getOpenIdByCode")
    public Result<?> getOpenIdByCode(String code) {
        String json = restTemplate.getForObject("https://api.weixin.qq.com/sns/jscode2session?appid=" + _appId + "&secret=" + _secret + "&js_code=" + code + "&grant_type=authorization_code", String.class);
        if (json != null) {
            JSONObject jsonObject = JSONObject.parseObject(json);
            if (jsonObject != null)
                return Result.OK(jsonObject);
        }

        return Result.error(500, "获取openId失败");
    }
}
